/*
 *  Copyright (C) 2004 Cidero, Inc.
 *
 *  Permission is hereby granted to any person obtaining a copy of 
 *  this software to use, copy, modify, merge, publish, and distribute
 *  the software for any non-commercial purpose, subject to the
 *  following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY IN CONNECTION WITH THE SOFTWARE.
 * 
 *  File: $RCSfile: HTTPProxyServer.java,v $
 *
 */

package com.cidero.bridge;

import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Logger;

import com.cidero.http.*;
import com.cidero.util.ShoutcastOutputStream;
import com.cidero.util.AppPreferences;


/**
 * HTTP server class for use with MediaRenderer bridge
 *
 */
public class HTTPProxyServer implements HTTPRequestListener
{
  private static Logger logger = Logger.getLogger("com.cidero.bridge");

  MediaRendererBridge bridge = null;  // 'parent' bridge instance
  HTTPServer server = null;
  ArrayList sessionList = new ArrayList();

  /**
   *  Constructor for the proxy. An HTTP server is started up on
   *  the specified port, and left running for the duration of the
   *  process  
   *
   *  @param  port  Port number of the proxy server
   */
  public HTTPProxyServer( MediaRendererBridge bridge, int port )
    throws IOException
  {
    this.bridge = bridge;
    
		int nHostAddrs = HostInterface.getNHostAddresses();

    // Assume only one host interface for now - (OJN)

    String bindAddr = HostInterface.getHostAddress(0);

    server = new HTTPServer();

    logger.fine("HTTPServer: Opening server at bindAddr: " + bindAddr +
                       " port: " + port );
    if( server.open( bindAddr, port ) == false )
    {
      server.close();
      server = null;
      throw new IOException( "HTTPServer: Couldn't open server at port " + 
                             port );
    }

    server.addRequestListener( this );

    server.start();
  }
  
  /**
   *  process the http request, 
   *
   *  @return true, so HTTP server thread will close connection on return
   */
  public boolean httpRequestReceived( HTTPRequest httpReq )
  {
    logger.fine( "HTTPServer: httpRequestReceived" );
    logger.fine( httpReq.toString() );

		if( httpReq.isGetRequest() == true )
    {
			return processHTTPGetRequest( httpReq );
		}

		if( httpReq.isPostRequest() == true )
    {
			processHTTPPostRequest( httpReq );
      return true;
		}

    logger.fine( "HTTPProxy: httpRequestReceived - unknown request" );

		httpReq.returnBadRequest();
    return true;
  }

  /**
   *  Process GET request from media playback device. The GET request
   *  has encoded in it the name of the 'virtual radio station', which
   *  should connect to a named queue in this bridge
   *  
   *  @return  true, if done with socket, false if HTTP server thread should
   *           leave socket open since another thread is now using it
   */
	private boolean processHTTPGetRequest( HTTPRequest httpReq )
	{
    //
    // Find playback 'session' that matches this incoming request
    // The session is simply the tail end of the URI. The syntax 
    // of the URI is (early idea anyway):
    //
    //   http://host:port/deviceClass/deviceSubClass/deviceName
    //
    // For example, for the 1st Prismiq device in a home:
    //
    // the URI would be 'http:192.168.1.106/UPnPBridge/renderer/Prismiq-1'
    //
    // Shoutcast (home radio) station 'Homecast1' would be: 
    //
    // the URI would be 'http:192.168.1.106/UPnPBridge/shoutcast/Homecast1'
    //

    logger.fine("HTTPServer: Get Request, URI = " + httpReq.getResource() );
     
    String urlPath;
    MediaRendererSession session;

    session = MediaRendererSession.
                findSessionByProxyUrlPath( httpReq.getResource() );
    if( session == null )
    {
      logger.warning("No proxy session found for " + httpReq.getResource() );
      httpReq.returnBadRequest();
      return true;
    }
    
    logger.fine("HTTPServer: Found matching session");

    //
    // Check to see if client is requesting shoutcast-style metadata
    // Note: Audiotron doesn't send the Icy-Metadata header as far as
    // I can tell, but does send an 'Accept: */*'
    boolean shoutcastMode = false;
    //boolean shoutcastMode = true;
    
    HTTPHeader hdr = httpReq.getHeader( "Icy-Metadata" );
    if( hdr != null )
    {
      logger.fine( "HTTPServer: Found shoutcast header!!!!!!!!!!!!!!!!!!!");
      if( hdr.getValue().equals("1") )
      {
        logger.fine( "HTTPServer: Enabling shoutcast mode");
        shoutcastMode = true;
      }
    }
    else
    {
      logger.fine( "!!!!!!!!!!!!!!!HTTPServer: No shoutcast header (Icy-MetaData) found");
    }
    
		HTTPResponse httpResponse = new HTTPResponse();

		httpResponse.setStatusCode( HTTPStatus.OK );
    httpResponse.setContentType( "audio/mpeg" );
    httpResponse.setContentLength( 200000000 );
    
    // shoutcastMode = true;  // For sync testing with windows media player
    
    if( shoutcastMode )
    {
      // Set things up to insert metadata every 8192 bytes (~0.5 sec)
      // It only gets inserted when the song title changes...
      logger.fine( "HTTPServer: setting icy-metaint header to 8192");
      httpResponse.addHeader( "icy-metaint", "8192" );
      httpResponse.addHeader( "icy-name", "IcyName" );
    }

    logger.fine("HTTPServer Response Header:\n" + 
                       httpResponse.toString() );

    logger.fine("HTTPServer: Posting Header");
    HTTPConnection connection = httpReq.getConnection();
    try
    {
      connection.sendResponseHeader( httpResponse );
    }
    catch( IOException e )
    {
      logger.warning("HTTPServer: Error sending response header");
      return true;
    }
    
    //
    // Get the underlying stream from the HTTP socket and write the 
    // bytes from the queue to the socket until the queue is shut down
    // (end of session)
    //
    ShoutcastOutputStream outStream;

    //shoutcastMode = false;
    if( shoutcastMode )
    {
      outStream = new ShoutcastOutputStream( connection.getOutputStream(),
                                             8192 );
    }
    else
    {
      outStream = new ShoutcastOutputStream( connection.getOutputStream(),
                                             0 );  // disabled shoutcast mode
    }
    
    //
    // Join the synchronous shoutcast group for the proxy session
    // synchronous shoutcast group and run it in current thread.
    // Subsequent requests are handled by adding streams to the
    // group.  Routine only returns a valid group for the first 
    // thread that joins it, otherwise null. Subsequent threads are 
    // 'joined' with first thread
    //
    SyncShoutcastGroup syncShoutGroup = 
      session.joinSyncShoutcastGroup( outStream, connection );

    if( syncShoutGroup == null )
    {
      // Joined existing session - return false to terminate this thread while
      // leaving socket open
      return false;  
    }

    // 1st client in session - start up synchronous shoutcast output.
    // This routine returns when all clients stop reading the output stream
    session.runSyncShoutcast();

    return true;
	}

	private void processHTTPPostRequest( HTTPRequest httpReq )
	{
		HTTPResponse httpResponse = new HTTPResponse();

    httpResponse.setContentType( HTML.CONTENT_TYPE );

		httpResponse.setStatusCode( HTTPStatus.OK );
		//httpResponse.setContent( "Hello" );

    logger.fine("HTTP POST Response:\n" + httpResponse.toString() );
    
		//httpReq.post( httpResponse );
  }


}


